package cn.net.xinyi.xs.dao.ibatis;

import java.io.Serializable;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
import org.springframework.util.ClassUtils;

import cn.net.xinyi.xs.dao.Dao;
import cn.net.xinyi.xs.dao.ibatis.ext.CustomSqlExecutor;
import cn.net.xinyi.xs.util.ReflectUtil;

import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.engine.execution.SqlExecutor;
import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;

/**
 * This class serves as the a class that can CRUD any object witout any Spring
 * configuration. The only downside is it does require casting from Object to
 * the object class.
 * 
 * @author Cai Hua
 */
public class BaseDaoiBatis extends SqlMapClientDaoSupport implements Dao {
	/**
	 * Log variable for all child classes. Uses LogFactory.getLog(getClass())
	 * from Commons Logging
	 */
	protected final Log log = LogFactory.getLog(getClass());

	private SqlExecutor sqlExecutor;

	public SqlExecutor getSqlExecutor() {
		return sqlExecutor;
	}

	public void setSqlExecutor(SqlExecutor sqlExecutor) {
		this.sqlExecutor = sqlExecutor;
	}

	public void setEnableLimit(boolean enableLimit) {
		if (sqlExecutor instanceof CustomSqlExecutor) {
			((CustomSqlExecutor) sqlExecutor).setEnableLimit(enableLimit);
		}
	}

	public void initialize() throws Exception {
		if (sqlExecutor != null) {
			SqlMapClient sqlMapClient = getSqlMapClientTemplate()
					.getSqlMapClient();
			if (sqlMapClient instanceof ExtendedSqlMapClient) {
				ReflectUtil.setFieldValue(((ExtendedSqlMapClient) sqlMapClient)
						.getDelegate(), "sqlExecutor", SqlExecutor.class,
						sqlExecutor);
			}
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public List getAll(Class clazz) {
		return getSqlMapClientTemplate().queryForList(
				iBatisDaoUtils.getSelectQuery(ClassUtils.getShortName(clazz)),
				null);
	}

	/**
	 * {@inheritDoc}
	 */
	public Object get(Class clazz, Serializable primaryKey) {
		Object object = getSqlMapClientTemplate().queryForObject(
				iBatisDaoUtils.getFindQuery(ClassUtils.getShortName(clazz)),
				primaryKey);
		if (object == null) {
			throw new ObjectRetrievalFailureException(ClassUtils
					.getShortName(clazz), primaryKey);
		}
		return object;
	}

	/**
	 * {@inheritDoc}
	 */
	public Object save(final Object object) {
		String className = ClassUtils.getShortName(object.getClass());
		Object primaryKey = iBatisDaoUtils.getPrimaryKeyValue(object);
		String keyId = null;

		// check for null id
		if (primaryKey != null) {
			keyId = primaryKey.toString();
		}

		// check for new record
		if (StringUtils.isBlank(keyId)) {
			iBatisDaoUtils.prepareObjectForSaveOrUpdate(object);
			primaryKey = getSqlMapClientTemplate().insert(
					iBatisDaoUtils.getInsertQuery(className), object);

			// check for null id
			if (primaryKey != null) {
				keyId = primaryKey.toString();
			}
			iBatisDaoUtils.setPrimaryKey(object, Long.class, new Long(keyId));
		} else {
			iBatisDaoUtils.prepareObjectForSaveOrUpdate(object);
			getSqlMapClientTemplate().update(
					iBatisDaoUtils.getUpdateQuery(className), object);
		}

		// check for null id
		if (iBatisDaoUtils.getPrimaryKeyValue(object) == null) {
			throw new ObjectRetrievalFailureException(className, object);
		} else {
			return object;
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public void remove(Class clazz, Serializable primaryKey) {
		getSqlMapClientTemplate().update(
				iBatisDaoUtils.getDeleteQuery(ClassUtils.getShortName(clazz)),
				primaryKey);
	}
}
